(function($){

  KOR.Product = KOR.createClass({

	DISABLED: 0,
	ACTIVE: 1,
	SELECTED: 2,
	SWATCH_VIEW: 'SWATCH',
	SWATCH_THN_SIZE: 'thumb',
	SWATCH_REG_SIZE: 'std',
	SWATCH_EHN_SIZE: 'vbig',
	ALT_THN_SIZE: 'thumb',
	ALT_REG_SIZE: 'std',
	ALT_EHN_SIZE: 'vbig',
	DEFAULT_VIEW: 'front',

	//constructor
	init: function(id, data, bus){
		this.id = id;
		this.data = $.extend(true, {}, data);
		this.bus = bus;
		this.selectionCascade = [];
		this.selectedAttributeVariations = {};
		this.cartableProductVariants = $.extend(true, {}, this.data.productVariations);
		this.selectedProdutImages = [];
		this.productPrice = {'regular': this.data.productPrice};
		this.currentView = this.DEFAULT_VIEW;
		this.updateProductImages();
		this.updateImageSets();
	},

	//getters
	getProductImages: function(){
		return this.selectedProductImages;
	},

	getSwatch: function(attribute, val, size, view) {
		return KOR.detect(this.data.variationAttributes[attribute].variations[val].images, function(image){
			return image.size == (size || this.SWATCH_THN_SIZE) && image.view == (view || this.SWATCH_VIEW);
		}, this);
	},

	getProductImage: function(size, view){
		var obj = this;
		var viewImage = this.getStrictProductImage(size, view);
		if(!viewImage) viewImage = this.getPrimaryProductImage(size);
		if(!viewImage) viewImage = this.getFallbackProductImage(size);

		return viewImage;
	},

	getProductZoomImage: function(zoomSize, fallbackSize) {
		var image = this.getStrictProductImage(zoomSize);
		if (!image && !this.currentView) {
			image = this.getPrimaryProductImage(zoomSize);
		}
		if (!image && fallbackSize) image = this.getStrictProductImage(fallbackSize);
		if (!image) image = this.getLargestProductImageInView();
		if (!image && fallbackSize) image = this.getEnhancedProductImage(fallbackSize);
		if (!image) image = this.getEnhancedProductImage();
		if (!image) image = this.getProductImage();
		return image;
	},

	getProductZoomRotateSet: function(size, fallbackSize) {
		var image = this.getProductZoomImage(size, fallbackSize),
			tag = 'set-'.uId();
		image.setTag = tag;
		var imageSet = [image];
		if (image.sets && image.sets.length) {
			var set = image.sets[0],
				props = [],
				p, i;
			for (p in this.selectedAttributeVariations) {
				if (this.selectedAttributeVariations.hasOwnProperty(p)) {
					props.push(p);
					break;
				}
			}
			if (props.length) {
				// Get the set images from the selected variation
				for (v in this.data.productVariations) {
					if (this.data.productVariations.hasOwnProperty(v)) {
						var matched = true;
						for (i = 0; i < props.length; i++) {
							if (!this.data.productVariations[v][props[i]]) {
								matched = false;
								break;
							}
						}
						if (matched) {
							var vImages = this.data.productVariations[v][props[i]].images;
							for (i = 0; i < vImages.length; i++) {
								if (vImages[i].sets) {
									for (var q = 0; q < vImages[i].sets.length; q++) {
										if (vImages[i].sets[q] == set && vImages[i].size == image.size) {
											imageSet.push(vImages[i]);
											break;
										}
									}
								}
							}
							break;
						}
					}
				}
			}
			else {
				// No variation selected; get set imges from default image array
				var numImages = this.data.images.length;
				for (i = 0; i < numImages; i++) {
					if (this.data.images[i].sets) {
						for (var q = 0; q < this.data.images[i].sets.length; q++) {
							if (this.data.images[i].sets[q] == set && this.data.images[i].size == image.size) {
								imageSet.push(this.data.images[i]);
								break;
							}
						}
					}
				}
			}
		}
		var count = imageSet.length;
		if (count > 1) {
			// Remove the first element; it's a duplicate
			imageSet.shift();
			// Cut the deck until the target view image is first
			while (--count) {
				if (imageSet[0].setTag && imageSet[0].setTag == tag) {
					break;
				}
				imageSet.push(imageSet.shift());
			}
		}
		return imageSet;
	},

	getStrictProductImage: function(size, view) {
		return KOR.detect(this.selectedProductImages, function(image){
			return image.size == (size || this.ALT_REG_SIZE) && image.view == (view || this.currentView);
		}, this);
	},

	getPrimaryProductImage: function(size) {
		return KOR.detect(this.selectedProductImages, function(image){
			return image.size == (size || this.ALT_REG_SIZE) && image.primary == true;
		}, this);
	},

	getFallbackProductImage: function(size) {
		return KOR.detect(this.selectedProductImages, function(image){
			return image.size == (size || this.ALT_REG_SIZE);
		}, this);
	},

	getLargestProductImageInView: function(view) {
		var viewImg = this.selectedProductImages.uPluck(function(image) {
				if (image.view == (view || this.currentView))
					return image;
				return undefined;
			}, this),
			count = viewImg.length,
			width = -1,
			image = null;
		for (var i = 0; i < count; i++) {
			if (viewImg[i].view == (view || this.currentView) &&
					parseInt(viewImg[i].width) > width) {
				image = viewImg[i];
				width = parseInt(image.width);
			}
		}
		return image;
	},

	getEnhancedProductImage: function(size, view){
		return this.getProductImage(size || this.ALT_EHN_SIZE, view)
	},

	getThumbProductImage: function(size, view){
		return this.getProductImage(size || this.ALT_THN_SIZE, view)
	},

	getProductImagesThumbs: function(size){
		return KOR.pluck(this.selectedProductImages, function(image){
			return image.size == (size || this.ALT_THN_SIZE) ? image : undefined;
		}, this);
	},

	getProductPrice: function(){
		return this.productPrice;
	},

    getCartablePrice: function(){
        if(this.productPrice && this.productPrice.sale){
            return this.productPrice.sale.min || this.productPrice.sale.regular;
        }
        return this.productPrice.list.min || this.productPrice.list.regular || "";
	},

	getSelectionCascade: function(){
		var obj = this;
		var order = [].concat(obj.selectionCascade);
		$.each(obj.data.variationAttributes, function(variationAttributeKey,variationAttribute){
		  if(!KOR.detect(obj.selectionCascade, function(selection){
				  return selection == variationAttributeKey;
		  })){
			order.push(variationAttributeKey)
		  }
		});
		return order;
	},

	getCartableProductVariants: function(){
		return this.cartableProductVariants;
	},

	getSelectedAttributeVariation: function(attribute){
		return this.selectedAttributeVariations[attribute];
	},

	getSelectedAttributeVariationDisplayName: function(attribute){
		return this.getSelectedAttributeVariation(attribute) ? this.getSelectedAttributeVariation(attribute).variationName : undefined
	},

	/**
	 * Gets the paroduct SKU of the selected variant if there is one, or the
	 * SKU of the youngest parent that includes all currently selected
	 * variants.
	 *
	 * @return string The SKU of a variant that most accurately represents the
	 * 		current selection.
	 */
	getBestVariantSKU: function(){
		var sku = this.getCartableProductVariant();
		if (!sku)
			sku = this.data;
		return sku.productSKU;
	},

    getCartableProductVariant: function(ev){
        var productVariants = this.getCartableProductVariants();
        var sku = undefined;
        if(KOR.count(productVariants) == 1){
            $.each(productVariants, function(key, product) {
                sku = product;
            });
        }
        return sku;
    },

    getFirstCartableProductVariant: function(ev){
        var productVariants = this.getCartableProductVariants();
        var sku = undefined;
        for (product in productVariants) {
	        return productVariants[product];
		}
		return false;
    },
	
	//setters
	setSelectedAttributeVariation: function(attribute, variationValue){
		if(variationValue == ''){
			delete this.selectedAttributeVariations[attribute];
		}else{
			this.selectedAttributeVariations[attribute] = this.data.variationAttributes[attribute].variations[variationValue];
		}
		return this;
	},

	setSelectionCascade: function(attribute){
	  if(!this.selectionCascade.uDetect(function(v){
		return v == attribute;
	  })){
		this.selectionCascade.push(attribute);
	  }
	},

	updateSelectionCascade: function(attribute){
		this.getSelectedAttributeVariation(attribute) == undefined ? this.removeSelectionCascade(attribute) : this.setSelectionCascade(attribute);
	},

	updateProductImages: function(){
		var obj = this;
		var currentImageGroup = obj.selectedProductImages;
		var high = 0;
		obj.selectedProductImages = obj.data.images;

		$.each(obj.data.productVariations, function(key, productVariant){
			var weight = 0;
			$.each(obj.selectionCascade, function(i,selectionName){
				weight += ((obj.getSelectedAttributeVariationDisplayName(selectionName) == productVariant[selectionName]) &&
					productVariant.images && productVariant.images.length>0 &!
					(obj.data.variationAttributes[selectionName].hasProductImagery != undefined &&
					 obj.data.variationAttributes[selectionName].hasProductImagery==false))
					? Math.pow(obj.selectionCascade.length-i, obj.selectionCascade.length-i) : 0;
			});
			if(weight > high){
				high = weight;
				obj.selectedProductImages = productVariant.images;
			}
		});

		if(currentImageGroup != obj.selectedProductImages){
			this.updateImageSets();
			this.fire('changeImageGroup');
			this.fire('changeProductImage');
		}
	},
	
	updateImageSets: function() {
		var obj = this;
		this.selectedProductSets = {};
		$.each(obj.selectedProductImages, function(key, image) {
			if (image.sets) {
				$.each(image.sets, function(idx, set) {
					if (!obj.selectedProductSets[set]) {
						obj.selectedProductSets[set] = {};
					}
					if (!obj.selectedProductSets[set][image.size]) {
						obj.selectedProductSets[set][image.size] = [];
					}
					obj.selectedProductSets[set][image.size].push(image);
				});
			}
		});
		this.fire('changeImageSets');
	},
	
	getNumImagesInSet: function(size, setName) {
		size = size || this.ALT_REG_SIZE;
		if (!setName) {
			// Determine the set of the current view & size image
			var image = this.getProductImage(size);
			if (!image || !image.sets || !image.sets.length) {
				return 0;
			}
			setName = image.sets[0];
		}
		if (!this.selectedProductSets[setName] || !this.selectedProductSets[setName][size]) {
			return 0;
		}
		return this.selectedProductSets[setName][size].length;
	},
	
	getViewAtSetPosition: function(position, size, setName) {
		size = size || this.ALT_REG_SIZE;
		if (!setName) {
			// Determine the set of the current view & size image
			var image = this.getProductImage(size);
			if (!image || !image.sets || !image.sets.length) {
				return false;
			}
			setName = image.sets[0];
		}
		if (!this.selectedProductSets[setName] ||
				!this.selectedProductSets[setName][size] ||
				!this.selectedProductSets[setName][size][position]) {
			return false;
		}
		return this.selectedProductSets[setName][size][position].view;
	},

	updateProductPrice: function() {
		var obj = this;
		var productPrice = {
			list: {},
			sale: {}
		};
		var parseNumber = function (str){
			return parseFloat(str.replace(/\W/g,function(){
				return arguments[0]=='.' ? '.' : '';
			}));
		};

		var parsePriceType = function(productVariant, type){
			if(productVariant[type+'Price'] != undefined){
				var pnumber = parseNumber(productVariant[type+'Price']);
				if(!productPrice[type].regular && !productPrice[type].min && !productPrice[type].max){
					productPrice[type].regular = productVariant[type+'Price'];
				} else if (!productPrice[type].regular || productPrice[type].regular != productVariant[type+'Price']) {
					if(!productPrice[type].min && !productPrice[type].max){
						productPrice[type].min = productPrice[type].regular;
						productPrice[type].max = productPrice[type].regular;
						delete productPrice[type].regular;
					}
					if(pnumber < parseNumber(productPrice[type].min)){
						productPrice[type].min = productVariant[type+'Price'];
					} else if (pnumber > parseNumber(productPrice[type].max)) {
						productPrice[type].max = productVariant[type+'Price'];
					}
				}
			}
		}

		$.each(obj.getCartableProductVariants(), function(i, productVariant){
			parsePriceType(productVariant, 'list');
			parsePriceType(productVariant, 'sale');
		});

		if(!productPrice.list.regular && !productPrice.list.min && !productPrice.list.max){
			productPrice.list.regular = obj.data.listPrice;
		}
		if(!productPrice.sale.regular && !productPrice.sale.min && !productPrice.sale.max){
			productPrice.sale.regular = obj.data.salePrice;
            if(!productPrice.sale.regular){
                delete productPrice.sale;
            }
		}
        if(productPrice.sale && KOR.encodeJSON(productPrice.sale) == KOR.encodeJSON(productPrice.list)){
            delete productPrice.sale;
        }

		if(KOR.encodeJSON(productPrice) != KOR.encodeJSON(obj.productPrice)){
			obj.productPrice = productPrice;
			obj.fire('changeProductPrice');
		}
	},

	updateDynamicBlocks: function(attribute, message){
		// Collect the IDs of all the dynamic blocks for this product
		var dynBlockElems = this.bus.find('[data-dynamic-block-name][data-dynamic-block-id]', {target: this}),
			bIDs = [];
		dynBlockElems.each(function(idx, elem) {
			elem = $(elem);
			var bID = elem.attr('data-dynamic-block-id'),
				bName = elem.attr('data-dynamic-block-name');
			if (bID && bName) bIDs.push(bID);
		});
		// Create the query string
		var sku = this.getBestVariantSKU();
		var	qStr = 'SKU={{}}&cid={{}}'.uInject(sku, bIDs.join('&cid='));
		// Fire the request
		$.ajax({
			cache: true,
			complete: function() {
				this.fire('changeAttributeDynamicBlockComplete', {attribute: attribute, attributeValue: message});
			},
			context: this,
			data: qStr,
			dataType: 'html',
			error: function(jqXHR, textStatus, errorThrown) {
				KOR.warn('Component request on attribute change failed. Dynamic blocks will not be updated for this attribute change. Details: ' + textStatus + ' :: ' + errorThrown);
			},
			global: false,
			success: function(data, textStatus, jqXHR) {
				this.changeDynamicBlocks(bIDs, data);
			},
			url: KOR.store.urls.productAjaxRequest
		});
	},

	removeSelectionCascade: function(attribute){
		this.selectionCascade = this.selectionCascade.filter(function(selection){
			return selection == attribute ? false : selection;
		});
	},

	updateAttributeVariants: function() {
		var obj = this
		var changeEvents = {};
		var changeAvailableEvents = {};
		obj.cartableProductVariants = KOR.pluck(obj.data.productVariations, function(productVariation){
			return (productVariation.isAvailable && productVariation.inStock) ? productVariation : undefined;
		});

		$.each(obj.getSelectionCascade(), function(i,selectionName){

			$.each(obj.data.variationAttributes[selectionName].variations, function(i,attributeVariation){
				attributeVariation._bak_status = attributeVariation._status;
				attributeVariation._status = obj.DISABLED;
			});

			$.each(obj.cartableProductVariants, function(i,productVariant){
				var productVariantValue = obj.data.variationAttributes[selectionName].variations[productVariant[selectionName]];
				var status;
				if(obj.getSelectedAttributeVariation(selectionName) && productVariantValue == obj.getSelectedAttributeVariation(selectionName)){
					status = obj.SELECTED;
				} else {
					status = obj.ACTIVE;
				}
				if(obj.data.variationAttributes[selectionName].variations[productVariant[selectionName]]){
					obj.data.variationAttributes[selectionName].variations[productVariant[selectionName]]._status = status;
				}
			});

			var reduceProducts = obj.getSelectedAttributeVariation(selectionName);

			$.each(obj.data.variationAttributes[selectionName].variations, function(i,attributeVariation){
				if(attributeVariation._bak_status !== status){
					if(attributeVariation._bak_status == obj.SELECTED && attributeVariation._status == obj.DISABLED){
						obj.setSelectedAttributeVariation(selectionName, '');
						obj.updateSelectionCascade(selectionName);
						reduceProducts = false;
						changeEvents[selectionName] = '';
					}
					changeAvailableEvents[selectionName] = '';
				}
			});

			if(reduceProducts){
				obj.cartableProductVariants = KOR.pluck(obj.cartableProductVariants, function(productVariant){
					return (productVariant[selectionName] == obj.getSelectedAttributeVariation(selectionName).variationName && productVariant.isAvailable && productVariant.inStock) ? productVariant : undefined;
				});
			}
		});

		$.each(changeAvailableEvents, function(eventName,eventMessage){
			obj.fire('changeAttribute{{}}Availibility'.uInject(eventName.uClassName()), eventMessage);
			obj.fire('changeAttributeAvailibility', {attribute: eventName, attributeValue: eventMessage});
		});

		$.each(changeEvents, function(eventName,eventMessage){
			obj.fire('changeAttribute{{}}'.uInject(eventName.uClassName()), eventMessage);
			obj.fire('changeAttribute', {attribute: eventName, attributeValue: eventMessage});
		});
	},

	//events
	changeAttribute: function(attribute, message) {
		var methodName = 'changeAttribute{{}}'.uInject(attribute.uClassName());
		this[methodName] ? this[methodName](message) : this.changeAttributeDefault(attribute, message);
        return this;
	},

	changeAttributeView: function(message){
		this.currentView = message;
		this.fire('changeView');
		this.fire('changeProductImage');
	},

	changeAttributeDefault: function(attribute, message){
		this.fire('changeAttributeBegin', {attribute: attribute, attributeValue: message});
		this.setSelectedAttributeVariation(attribute, message);
		this.updateSelectionCascade(attribute);
		this.updateAttributeVariants();
		this.updateProductPrice();
		this.updateProductImages();
		this.fire('changeAttribute', {attribute: attribute, attributeValue: message});
		this.fire('changeAttribute{{}}'.uInject(attribute.uClassName()), message);
		this.fire('changeAttributeComplete', {attribute: attribute, attributeValue: message});
		this.updateDynamicBlocks(attribute, message);
	},

	changeDynamicBlocks: function(bIDs, data) {
		var _this = this;
		data = $(data);
		$.each(bIDs, function(idx, bID) {
			var wrapper = $("[data-dynamic-block-id='{{}}']".uInject(bID), data);
			wrapper.each(function(idx, elem) {
				elem = $(elem);
				var name = elem.attr('data-dynamic-block-name'),
					content = elem.html();
				_this.fire('changeDynamicBlock', {
					blockID: bID,
					blockName: name,
					blockData: content
				});
			});
		});
	},

	//facades
	fire: function(name, message){
	  KOR.eventMixin.fire.call(this.bus, name, message, this);
	},

	//booleans
	isSelectable: function(attribute, val) {
		if(this.data.variationAttributes[attribute].variations[val]){
		  return this.data.variationAttributes[attribute].variations[val]._status !==0 ? true : false;
		} else {
		  return true;
		}
	}
});

KOR.Product.get = function(id, data, bus){
  var key = 'instance_{{}}_{{}}'.uInject(id, bus.selector);
  if(!this[key])
	  this[key] = new this(id, data, bus);
	  return this[key]
  }

})(jQuery);
